/*
 * Licensed to the 南京汇海交通科技有限公司
 *
 * Copyright (c) 2025.
 * All rights reserved.
 *
 * 本软件属南京汇海交通科技有限公司所有，在未获得南京汇海交通科技有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.chframework.boot.core.client;

import com.chframework.common.exception.UtilityException;
import com.chframework.common.extend.ReactorHelper;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpCookie;
import org.springframework.http.ResponseCookie;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * webflux环境下向客户端写入Cookie的实现
 */
public interface ReactiveCookieScene {

    /**
     * 写入Cookie
     */
    static Mono<Void> writeWebCookie(BrowserCookie cookie) {
        return ReactorHelper.getContext()
                .map(ctx -> {
                    try {
                        String encodeCookieName = URLEncoder.encode(cookie.getName(), StandardCharsets.UTF_8.name());
                        ServerWebExchange exchange = ctx.get(ServerWebExchange.class);
                        ResponseCookie responseCookie = ResponseCookie.from(encodeCookieName, URLEncoder.encode(cookie.getValue(), StandardCharsets.UTF_8.name()))
                                .path(exchange.getRequest().getPath().contextPath().value() + "/")
                                .maxAge(cookie.getCookieMaxAge())
                                .httpOnly(cookie.isHttpOnly())
                                .secure("https".equalsIgnoreCase(exchange.getRequest().getURI().getScheme()))
                                .sameSite(cookie.getSameSite())
                                .build();
                        exchange.getResponse()
                                .getCookies()
                                .set(encodeCookieName, responseCookie);
                    } catch (Exception e) {
                        throw new UtilityException(e);
                    }
                    return ctx;
                })
                .thenEmpty(Mono.empty())
                .onErrorResume(e -> Mono.error(new UtilityException(e)));
    }

    /**
     * 读取Cookie
     */
    static Mono<BrowserCookie> readWebCookie(String cookieName) throws UtilityException {
        return ReactorHelper.getContext()
                .map(ctx -> {
                    try {
                        String encodeCookieName = URLEncoder.encode(cookieName, StandardCharsets.UTF_8.name());
                        ServerWebExchange exchange = ctx.get(ServerWebExchange.class);
                        MultiValueMap<String, HttpCookie> cookieMap = exchange.getRequest().getCookies();
                        List<HttpCookie> cookies = cookieMap.get(encodeCookieName);
                        if (cookies == null || cookies.size() <= 0) {
                            return new BrowserCookie(encodeCookieName, StringUtils.EMPTY, Duration.ZERO);
                        }

                        List<BrowserCookie> matchingCookieValues = new ArrayList<>();
                        for (HttpCookie cookie : cookies) {
                            if (!encodeCookieName.equals(cookie.getName())) {
                                continue;
                            }
                            String cookieValue = URLDecoder.decode(cookie.getValue(), StandardCharsets.UTF_8.name());
                            if (StringUtils.isBlank(cookieValue)) {
                                continue;
                            }
                            matchingCookieValues.add(new BrowserCookie(encodeCookieName, cookieValue, Duration.ZERO));
                        }

                        return matchingCookieValues.get(0);
                    } catch (Exception e) {
                        throw new UtilityException(e);
                    }
                })
                .onErrorResume(e -> Mono.error(new UtilityException(e)));
    }

}
